﻿using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Acl.DataCheck;

/// <summary>
/// 约束校验器
/// </summary>
internal abstract class ConstraintValidator
{
    public abstract void Validate(ValidationContext ctx);

    private static RangeValidator _rangeValidator = new();
    private static FKValidator _fkValidator = new();
    private static GroupUniqueIncreaseValidator _groupValidator = new();
    private static CheckValidator _checkValidator = new();
    private static RangeValidator _flagEnumValidator = new() { IsFlagEnum = true };
    private static ConditionFKValidator _conditionFkValidator = new();

    private static readonly StringBuilder tokenBuilder = new(64);

    private static Dictionary<string, string[]> _tokenCache = [];

    internal static string[] ParseToken(string expression)
    {
        Guard.NotNullOrEmpty(expression, nameof(expression));

        if (!_tokenCache.TryGetValue(expression, out var token))
        {
            _tokenCache[expression] = token = InternalParseToken(expression).ToArray();
        }

        return token;
    }

    private static IEnumerable<string> InternalParseToken(string input)
    {
        tokenBuilder.Clear();
        for (var i = 0; i < input.Length; i++)
        {
            var c = input[i];
            if (c is '(' or '（' or ')' or '）')
            {
                if (tokenBuilder.Length > 0)
                {
                    yield return tokenBuilder.ToString();
                    tokenBuilder.Clear();
                }

                yield return c.ToString();
            }
            else if (c is '>' or '=' or '<')
            {
                if (tokenBuilder.Length > 0)
                {
                    yield return tokenBuilder.ToString();
                    tokenBuilder.Clear();
                }

                if (i < input.Length - 1 && input[i + 1] == '=')
                {
                    i++;
                    yield return $"{c}=";
                }
                else
                    yield return c.ToString();
            }
            else if (c is '&') //&&
            {
                if (tokenBuilder.Length > 0)
                {
                    yield return tokenBuilder.ToString();
                    tokenBuilder.Clear();
                }

                if (i < input.Length - 1 && input[i + 1] == '&')
                {
                    i++;
                    yield return "&&";
                }
                else
                    yield return c.ToString();
            }
            else if (c is '|') //||
            {
                if (tokenBuilder.Length > 0)
                {
                    yield return tokenBuilder.ToString();
                    tokenBuilder.Clear();
                }

                if (i < input.Length - 1 && input[i + 1] == '|')
                {
                    i++;
                    yield return "||";
                }
                else
                    yield return c.ToString();
            }
            else if (c is ' ' or '\t' or '\r' or '\n' or ',' or '，')
            {
                if (tokenBuilder.Length > 0)
                {
                    yield return tokenBuilder.ToString();
                    tokenBuilder.Clear();
                }
            }
            else
            {
                tokenBuilder.Append(c);
            }
        }

        if (tokenBuilder.Length > 0)
        {
            yield return tokenBuilder.ToString();
        }
    }

    public static ConstraintValidator Create(ConstraintType type)
    {
        return type switch
        {
            ConstraintType.Range => _rangeValidator,
            ConstraintType.FK => _fkValidator,
            ConstraintType.GroupUniqueIncrease => _groupValidator,
            ConstraintType.Check => _checkValidator,
            ConstraintType.FlagEnum => _flagEnumValidator,
            ConstraintType.ConditionFK => _conditionFkValidator,
            _ => default,
        };
    }
}